package edu.pku.hawk.service.impl;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

import edu.pku.hawk.controller.detectmonitor.DetectReport;
import edu.pku.hawk.controller.util.Config;
import edu.pku.hawk.controller.util.Constants;
import edu.pku.hawk.controller.util.ReportQueue;
import edu.pku.hawk.service.FusionSer;
import edu.pku.hawk.service.bo.FusionResult;
import edu.pku.hawk.service.bo.FusionSensorData;

public class FusionSerImpl_Average implements FusionSer {

	public FusionResult fuse(FusionSensorData[] sdes) {
		return ifuse(sdes, true);
	}

	public FusionResult fuseSingle(FusionSensorData[] sde) {
		return ifuse(sde, false);
	}

	public FusionResult fuseAll(final HashMap<Integer, ReportQueue> reportQueueById) {
		FusionResult ret = new FusionResult();

		final int infrared_radio_base = 2;
		final int pressure_radio_base = 5;
		final int metal_radio_base = 10;

		// Location
		double infrared_ax = 0;
		double infrared_ay = 0;
		double pressure_ax = 0;
		double pressure_ay = 0;
		double metal_ax = 0;
		double metal_ay = 0;

		int infrared_count = 0;
		int pressure_count = 0;
		int metal_count = 0;

		// Typeset
		HashSet<Integer> typeset = new HashSet<Integer>();
		for (Iterator<Map.Entry<Integer, ReportQueue>> it = reportQueueById.entrySet().iterator(); it.hasNext();) {
			Map.Entry<Integer, ReportQueue> entry = it.next();
			int id = entry.getKey();
			ReportQueue q = entry.getValue();

			int type = q.get(0).dtctType;
			if (type != Constants.TYPE_TRACK && type != Constants.TYPE_RFID_SENSOR
					&& type != Constants.TYPE_SOUND_SENSOR) {
				typeset.add(type);
			}

			// int ini = Config.Singleton.Instance.MonitorBuffer / 4;
			int start = (q.length() > Config.MonitorBuffer) ? (q.length() - Config.MonitorBuffer) : 0;
			int end = (q.length() > Config.MonitorBuffer) ? Config.MonitorBuffer : q.length();
			for (int i = start; i < end; i++) {
				DetectReport sd = q.get(i);
				switch (sd.dtctType) {
				case Constants.TYPE_INFRARED_SENSOR:
					if ((Integer) sd.lastValue == 1) {
						infrared_ax += (double) (sd.x + sd.x1) / 2.0;
						infrared_ay += (double) (sd.y + sd.y1) / 2.0;
						infrared_count++;
					}
					break;
				case Constants.TYPE_METAL_SENSOR:
					if ((Integer) sd.lastValue == 1) {
						metal_ax += sd.x;
						metal_ay += sd.y;
						metal_count++;
					}
					break;
				case Constants.TYPE_PRESSURE_SENSOR:
					if ((Integer) sd.lastValue > sd.threshold) {
						pressure_ax += sd.x;
						pressure_ay += sd.y;
						pressure_count++;
					}
				default:
					break;
				}
			}
		}
		if (infrared_count > 0) {
			infrared_ax /= infrared_count;
			infrared_ay /= infrared_count;
		}
		if (metal_count > 0) {
			metal_ax /= metal_count;
			metal_ay /= metal_count;
		}
		if (pressure_count > 0) {
			pressure_ax /= pressure_count;
			pressure_ay /= pressure_count;
		}

		double infrared_radio = 0;
		double pressure_radio = 0;
		double metal_radio = 0;
		double radio_count = 0;
		if (typeset.contains(Constants.TYPE_INFRARED_SENSOR)) {
			radio_count += infrared_radio_base;
		}
		if (typeset.contains(Constants.TYPE_PRESSURE_SENSOR)) {
			radio_count += pressure_radio_base;
		}
		if (typeset.contains(Constants.TYPE_METAL_SENSOR)) {
			radio_count += metal_radio_base;
		}
		if (radio_count == 0) {
			return null;
		} else {
			infrared_radio = infrared_radio_base / radio_count;
			pressure_radio = pressure_radio_base / radio_count;
			metal_radio = metal_radio_base / radio_count;
		}

//		 double aax = (infrared_ax + metal_ax + pressure_ax) / 3.0;
//		 double aay = (infrared_ay + metal_ay + pressure_ay) / 3.0;

		double aax = infrared_radio * infrared_ax + metal_radio * metal_ax + pressure_radio * pressure_ax;
		double aay = infrared_radio * infrared_ay + metal_radio * metal_ay + pressure_radio * pressure_ay;

		Random r = new Random();
		aax = aax + r.nextDouble() * 4 - 2;
		aay = aay + r.nextDouble() * 4 - 2;
		
		ret.x = (int) aax;
		ret.y = (int) aay;

		ret.objType = Constants.TYPE_TRACE_TRUNK;
		// Sensor types
		List<String> soundSensorIds = Config.Singleton.Instance.getSensorIdListByType(Constants.TYPE_SOUND_SENSOR);
		for (String sid : soundSensorIds) {
			int id = Integer.valueOf(sid);
			if (reportQueueById.containsKey(id)) {
				ReportQueue queue = reportQueueById.get(id);
				if (queue != null && queue.length() > 0) {
					for (int i = 0; i < queue.length(); i++) {
						DetectReport report = queue.get(i);
						if ((Integer) report.lastValue > 0) {
							ret.objType = Constants.TYPE_TRACE_TANK;
							break;
						}
					}
				}
			}
		}

		// Sensor type
		if (typeset.size() > 1) {
			ret.sensorType = Constants.TYPE_TRACK;
		} else {
			ret.sensorType = typeset.iterator().next();
		}

		// Radius
		if (typeset.size() > 1) {
			if (typeset.size() < 3) {
				ret.radius = (int) (10.0);
			} else {
				ret.radius = (int) (4.0);
			}
		} else {
			ret.radius = (int) (2.0);
		}
		return ret;
	}

	private FusionResult ifuse(FusionSensorData[] sdes, boolean combined) {
		if (sdes == null || sdes.length == 0)
			return null;

		FusionResult ret = new FusionResult();

		// Typeset
		HashSet<Integer> typeset = new HashSet<Integer>();
		for (FusionSensorData data : sdes) {
			if (data.type != Constants.TYPE_TRACK && data.type != Constants.TYPE_RFID_SENSOR) {
				typeset.add(data.type);
			}
		}

		// Tank type
		boolean isTank = false;
		for (int i = 0; i < sdes.length; i++) {
			if (sdes[i].type == Constants.TYPE_SOUND_SENSOR) {
				if ((Integer) sdes[i].lastValue == 1) {
					isTank = true;
				}
			}
		}
		// Not can not know whether it is tank by sound sensor ... so
		isTank = true;
		ret.objType = (isTank) ? Constants.TYPE_TRACE_TANK : Constants.TYPE_TRACE_TRUNK;

		// Location
		double ax = 0;
		double ay = 0;
		int count = 0;
		for (int i = 0; i < sdes.length; i++) {
			FusionSensorData sd = sdes[i];
			switch (sd.type) {
			case Constants.TYPE_INFRARED_SENSOR:
				if ((Integer) sd.lastValue == 1) {
					ax += (double) (sdes[i].x + sdes[i].x1) / 2.0;
					ay += (double) (sdes[i].y + sdes[i].y1) / 2.0;
					count++;
				}
				break;
			case Constants.TYPE_METAL_SENSOR:
				if ((Integer) sd.lastValue == 1) {
					ax += sdes[i].x;
					ay += sdes[i].y;
					count++;
				}
				break;
			case Constants.TYPE_PRESSURE_SENSOR:
				// if ((Integer) sd.lastValue > Config.PressureThreshold) {
				ax += sdes[i].x;
				ay += sdes[i].y;
				count++;
				// }
			default:
				break;
			}
		}
		if (count > 0) {
			ax /= count;
			ay /= count;
		}

		ret.x = (int) ax;
		ret.y = (int) ay;

		// Sensor type
		if (combined) {
			ret.sensorType = Constants.TYPE_TRACK;
		} else {
			ret.sensorType = sdes[0].type;
		}

		// Radius
		Random r = new Random();
		if (combined) {
			if (typeset.size() < 3) {
				ret.radius = (int) (25.0 + r.nextDouble() * 7.0);
			} else {
				ret.radius = (int) (10.0 + r.nextDouble() * 3.4);
			}
		} else {
			ret.radius = (int) (16.0 + r.nextDouble() * 5.0);
		}
		return ret;
	}

}
